#include "UILoader.hpp"
#include <fmt/format.h>
#include <spdlog/spdlog.h>
#include <GL/glew.h>

UILoader::UILoader()
{
  refBuilder=Gtk::Builder::create();
  cssProvider=Gtk::CssProvider::create();
  try
  {
    refBuilder->add_from_file("reviewui1.ui");
    cssProvider->load_from_path("review1.css");
  }
  catch(...)
  {
    throw;
  }
  mainWindow=GetInstance<Gtk::Window>("mainWindow");
  btnFilter=GetInstance<Gtk::Button>("btnFilter");
  btnPlayControl=GetInstance<Gtk::Button>("btnPlayControl");
  listTask=GetInstance<Gtk::TreeView>("listTask");
  btnPauseControl=GetInstance<Gtk::Button>("btnPauseControl");
  btnOutput=GetInstance<Gtk::Button>("btnOutput");

  //设置ListTask
  m_refTaskTreeModel=Gtk::TreeStore::create(taskColumns);
  listTask->set_model(m_refTaskTreeModel);
  listTask->set_reorderable();
  listTask->append_column("名称",taskColumns.m_col_name);
  listTask->append_column("创建时间",taskColumns.m_col_time);
  listTask->signal_row_activated().connect(sigc::mem_fun(*this,&UILoader::on_listTask_row_activated));
  //listTask->signal_row_expanded().connect(sigc::mem_fun(*this,&UILoader::on_listTask_row_expend));

  //设置colPoseInfo
  m_listStore=Gio::ListStore<ModelColumns>::create();
  auto selection_model=Gtk::SingleSelection::create(m_listStore);
  colPoseInfo=GetInstance<Gtk::ColumnView>("colPoseInfo");
  m_listPoseInfo=Gio::ListStore<ModelColumns>::create();
  m_listPoseInfo_select=Gtk::NoSelection::create(m_listPoseInfo);
  colPoseInfo->set_model(m_listPoseInfo_select);
  colPoseInfo->set_reorderable(true);
  for(int i=0;i<25;i++)
    m_listPoseInfo->append(ModelColumns::create((Cood3D){0,0,0}));

//元编程，启动!!!!
#define CREATE_POSETABLE_DATA(COL_NAME,COL_OBJ) \
  { \
    auto factory_x=Gtk::SignalListItemFactory::create(); \
    factory_x->signal_setup().connect(sigc::mem_fun(*this,&UILoader::on_setup_label)); \
    factory_x->signal_bind().connect([](const Glib::RefPtr<Gtk::ListItem>& list_item){ \
      auto col=std::dynamic_pointer_cast<ModelColumns>(list_item->get_item()); \
      if(!col) \
        return; \
      auto label=dynamic_cast<Gtk::Label*>(list_item->get_child()); \
      if(!label) \
        return; \
      label->set_text(fmt::format("{}",col->COL_OBJ/*这边已经有类型推导了*/)); \
    }); \
    auto column=Gtk::ColumnViewColumn::create(COL_NAME,factory_x); \
    column->set_expand(); \
    colPoseInfo->append_column(column); \
  }

  CREATE_POSETABLE_DATA("关节名称", m_col_jointname)
  CREATE_POSETABLE_DATA("关节X", m_col_x)
  CREATE_POSETABLE_DATA("关节Y", m_col_y)
  CREATE_POSETABLE_DATA("关节Z", m_col_z)
  CREATE_POSETABLE_DATA("关节位移", m_col_jointoffset)
  CREATE_POSETABLE_DATA("X转角", m_col_rotatex)
  CREATE_POSETABLE_DATA("Y转角", m_col_rotatey)
  CREATE_POSETABLE_DATA("Z转角", m_col_rotatez)
  CREATE_POSETABLE_DATA("关节转角", m_col_rotatejoint)

#undef CREATE_POSETABLE_DATA

  auto factory_x=Gtk::SignalListItemFactory::create();
  factory_x->signal_setup().connect(sigc::mem_fun(*this,&UILoader::on_setup_label));
  factory_x->signal_bind().connect([](const Glib::RefPtr<Gtk::ListItem>& list_item){
    auto col=std::dynamic_pointer_cast<ModelColumns>(list_item->get_item());
    if(!col)
      return;
    auto label=dynamic_cast<Gtk::Label*>(list_item->get_child());
    if(!label)
      return;
    label->set_text(fmt::format("{}",col->m_col_x));
  });
  auto column=Gtk::ColumnViewColumn::create("X",factory_x);
  column->set_expand();
  colPoseInfo->append_column(column);

  auto createCameraImageDraw=[this](const char* name){
    CameraImageDraw draw;
    draw.drawing=GetInstance<Gtk::DrawingArea>(name);
    draw.mut=new std::mutex;
    draw.image=Gdk::Pixbuf::create(Gdk::Colorspace::RGB,false,8,1920,1080);
    return draw;
  };
  cameraDraw[0]=createCameraImageDraw("drawCamera1");
  cameraDraw[0].drawing->set_draw_func(sigc::bind(sigc::mem_fun(*this,&UILoader::on_CameraDraw),0));
  cameraDraw[1]=createCameraImageDraw("drawCamera2");
  cameraDraw[1].drawing->set_draw_func(sigc::bind(sigc::mem_fun(*this,&UILoader::on_CameraDraw),1));
  cameraDraw[2]=createCameraImageDraw("drawCamera3");
  cameraDraw[2].drawing->set_draw_func(sigc::bind(sigc::mem_fun(*this,&UILoader::on_CameraDraw),2));
  cameraDraw[3]=createCameraImageDraw("drawCamera4");
  cameraDraw[3].drawing->set_draw_func(sigc::bind(sigc::mem_fun(*this,&UILoader::on_CameraDraw),3));
  cameraDraw[4]=createCameraImageDraw("drawCamera5");
  cameraDraw[4].drawing->set_draw_func(sigc::bind(sigc::mem_fun(*this,&UILoader::on_CameraDraw),4));
  drawSMPL=createCameraImageDraw("drawSMPL");
  drawSMPL.drawing->set_draw_func(sigc::mem_fun(*this,&UILoader::on_SMPLDraw));
  gl3DPose=GetInstance<Gtk::GLArea>("gl3DPose");
  gl3DPose->signal_render().connect(sigc::mem_fun(*this,&UILoader::on_gl3dpose_render),true);
  gl3DPose->signal_resize().connect(sigc::mem_fun(*this,&UILoader::on_gl3dpose_resize));
  gl3DPose->set_auto_render();
  gl3DPose->set_required_version(4,5);  //设置OpenGL版本

  patchRefreshAllUI.connect(sigc::mem_fun(*this,&UILoader::on_refreshALLUI));
  btnPlayControl->signal_clicked().connect(sigc::mem_fun(*this,&UILoader::on_btnPlayControl_click));
  btnPauseControl->signal_clicked().connect(sigc::mem_fun(*this,&UILoader::on_btnPauseControl_click));
}

UILoader::~UILoader()
{
  for(auto & i : cameraDraw)
    delete i.mut;
  delete drawSMPL.mut;
}

void UILoader::app_show(Glib::RefPtr<Gtk::Application> & app)
{
  app->signal_activate().connect([this,&app](){
      app->add_window(*mainWindow);
      mainWindow->set_visible(true);
  });
  app->run();
}

template<class T>
T* UILoader::GetInstance(const char* name)
{
  T* ptr=refBuilder->get_widget<T>(name);
  if(ptr==nullptr)
    throw std::runtime_error(fmt::format("无法获取{}实例",name));
  ptr->set_name(name);
  ptr->get_style_context()->add_provider(cssProvider,1);
  return ptr;
}

bool UILoader::on_gl3dpose_render(const Glib::RefPtr<Gdk::GLContext> & context)
{
  if(context->get_api()!=Gdk::GLAPI::GL)
    spdlog::error("OpenGL上下文类型不是GL");
  if(gl3dPoseFirstRender)
  {
    GLenum res=glewInit();
    if(res != GLEW_OK && res != GLEW_ERROR_NO_GLX_DISPLAY)
    {
      throw std::runtime_error(fmt::format("Can't Init GLEW {}",(const char*)glewGetErrorString(res)));
    }
    funcInitPose3D(gl3DPose->get_width(),gl3DPose->get_height());
    gl3dPoseFirstRender=false;
  }
  funcDisplayPose3D();
  return true;
}

void UILoader::on_gl3dpose_resize(int width, int height)
{
  funcResizePose3D(width,height);
}

void UILoader::RefreshUI()
{
  patchRefreshAllUI.emit();
}

void UILoader::on_refreshALLUI()
{
  gl3DPose->queue_render();
  drawSMPL.drawing->queue_draw();
  for(auto & i : cameraDraw)
    i.drawing->queue_draw();
  colPoseInfo->set_model(Gtk::NoSelection::create(m_listPoseInfo));
  colPoseInfo->queue_draw();
}

void UILoader::DisplayCamera(char* data,int width,int height,int index)
{
  std::lock_guard<std::mutex> lck(*cameraDraw[index].mut);
  cameraDraw[index].image=Gdk::Pixbuf::create_from_data(
    (const guint8*)data,
    Gdk::Colorspace::RGB,
    false,
    8,
    width,
    height,
    width*3,
    [](const guint8* data){delete [] data;}
  );
}

void UILoader::DisplaySMPL(char* data,int width,int height)
{
  std::lock_guard<std::mutex> lck(*drawSMPL.mut);
  drawSMPL.image=Gdk::Pixbuf::create_from_data(
    (const guint8*)data,
    Gdk::Colorspace::RGB,
    false,
    8,
    width,
    height,
    width*3,
    [](const guint8* data){delete [] data;}
  );
}

void UILoader::DisplayCoord(const std::array<Cood3D,25> & data)
{
  for(int i=0;i<25;i++)
  {
    auto item=m_listPoseInfo->get_item(i);
    item->m_col_x=data[i].x;
    item->m_col_y=data[i].y;
    item->m_col_z=data[i].z;
  }
}

void UILoader::on_btnPlayControl_click()
{
  //播放视频
  if(funcPlayStatusControl())
    btnPlayControl->set_label("停止");
  else
    btnPlayControl->set_label("开始");
}

void UILoader::on_CameraDraw(const Cairo::RefPtr<Cairo::Context>& cr,int width,int height,int index)
{
  std::lock_guard<std::mutex> lck(*cameraDraw[index].mut);
  if(!cameraDraw[index].image)
  {
    cr->set_source_rgb(1,1,1);
    cr->rectangle(0,0,width,height);
    cr->fill();
    cr->paint();
  }
  else
  {
    Gtk::DrawingArea* ptr=cameraDraw[index].drawing;
    double scalePara=(double)height/cameraDraw[index].image->get_height();
    Gdk::Cairo::set_source_pixbuf(cr, 
      cameraDraw[index].image->scale_simple(
        cameraDraw[index].image->get_width()*scalePara,height,
        Gdk::InterpType::NEAREST),
      0, 
      0);
    cr->paint();
  }
}

void UILoader::on_SMPLDraw(const Cairo::RefPtr<Cairo::Context>& cr,int width,int height)
{
  std::lock_guard<std::mutex> lck(*drawSMPL.mut);
  if(!drawSMPL.image)
  {
    cr->set_source_rgb(1,1,1);
    cr->rectangle(0,0,width,height);
    cr->fill();
    cr->paint();
  }
  else
  {
    Gtk::DrawingArea* ptr=drawSMPL.drawing;
    double scalePara=(double)height/drawSMPL.image->get_height();
    Gdk::Cairo::set_source_pixbuf(cr, 
        drawSMPL.image->scale_simple(
        drawSMPL.image->get_width()*scalePara,
        height,
        Gdk::InterpType::NEAREST),
      0, 
      0);
    cr->paint();
  }

}

void UILoader::on_setup_label(const Glib::RefPtr<Gtk::ListItem>& list_item)
{
  list_item->set_child(*Gtk::make_managed<Gtk::Label>("",Gtk::Align::CENTER));
}

void UILoader::DisplayList(const std::deque<std::string> & list)
{
  m_listStore->remove_all();
  for(const auto& i : list)
  {
    m_listStore->append(ModelColumns::create(i));
  }
}

void UILoader::SwitchButtonToStop()
{
  btnPlayControl->set_label("开始拍摄");
}

void UILoader::on_listTask_row_activated(const Gtk::TreeModel::Path& path,Gtk::TreeViewColumn* column)
{
  const auto iter=m_refTaskTreeModel->get_iter(path);
  if(iter)
  {
    const auto row=*iter;
    switch(row[taskColumns.level])
    {
      case 1:
        funcSetRecordChange(row[taskColumns.path]);
      break;
    }
  }
}

void UILoader::SetTaskList(const std::vector<std::tuple<long,std::string,std::string>>& list)
{
  listTask->remove_all_columns();
  listTask->append_column("名称",taskColumns.m_col_name);
  listTask->append_column("创建时间",taskColumns.m_col_time);
  for(const auto & i : list)
  {
    auto row=*(m_refTaskTreeModel->append());
    row[taskColumns.level]=0;
    row[taskColumns.ID]=std::get<0>(i);
    row[taskColumns.m_col_name]=std::get<1>(i);
    row[taskColumns.m_col_time]=std::get<2>(i);

    //获取子行
    const auto recList=funcGetRecordList(std::get<0>(i));
    for(const auto & j : recList)
    {
      auto rowChild=*(m_refTaskTreeModel->append(row.children()));
      rowChild[taskColumns.m_col_name]=std::to_string(std::get<0>(j));
      rowChild[taskColumns.m_col_time]=std::get<1>(j);
      rowChild[taskColumns.level]=1;
      rowChild[taskColumns.ID]=std::get<0>(j);
      rowChild[taskColumns.path]=std::get<2>(j);
    }
  }
}

void UILoader::on_listTask_row_expend(const Gtk::TreeModel::iterator& iter, const Gtk::TreeModel::Path& path)
//在展开时从数据库中读取录制数据
{
  if(iter->get_value(taskColumns.level)!=0)
    return;
  for(auto & i : iter->children())
  {
    Gtk::TreePath path(i.get_iter());
    m_refTaskTreeModel->row_deleted(path);
  }
  const auto recList=funcGetRecordList((*iter)[taskColumns.ID]);
  for(const auto & i : recList)
  {
    auto row=*(m_refTaskTreeModel->append(iter->children()));
    row[taskColumns.m_col_name]=std::to_string(std::get<0>(i));
    row[taskColumns.m_col_time]=std::get<1>(i);
    row[taskColumns.level]=1;
    row[taskColumns.ID]=std::get<0>(i);
    row[taskColumns.path]=std::get<2>(i);
  }
}

void UILoader::on_btnPauseControl_click()
{
  if(funcPauseControl())
    btnPauseControl->set_label("继续");
  else
    btnPauseControl->set_label("暂停");
}


void UILoader::on_btnOutput_click()
{
  auto dialog = Gtk::FileDialog::create();

  dialog->select_folder([this,dialog](const Glib::RefPtr<Gio::AsyncResult>& result){
    try
    {
      auto folder = dialog->select_folder_finish(result);
      auto select=listTask->get_selection();
      auto path=select->get_selected_rows();
      if(path.size()==0)
        return;
      const auto iter=m_refTaskTreeModel->get_iter(path[0]);
      if(iter)
      {
        const auto row=*iter;
        switch(row[taskColumns.level])
        {
          case 1:
            funcSetRecordChange(row[taskColumns.path]);
            funcOutput(row[taskColumns.ID],folder->get_path());
          break;
        }
      }
    }
    catch (const Gtk::DialogError& err)
    {
      return;
    }
    catch (const Glib::Error& err)
    {
      return;
    }
  });
}

